namespace System {
	/// <summary>
	/// The Money class deals with issues related to currencies including rounding 
	/// to nearest cent and splitting money into parts so that cents are not lost.
	/// </summary>
	public struct Money : IComparable {
		#region Constructors
		/// <summary>
		/// Creates a new <see cref="Money"/> instance.
		/// </summary>
		/// <param name="amount">Amount.</param>
		public Money(decimal amount) {
			m_amount = Math.Round(amount, FRACTION_DIGITS);
		}

		/// <summary>
		/// Creates a new <see cref="Money"/> instance.
		/// </summary>
		/// <param name="m">Money.</param>
		public Money(Money m) {
			m_amount = m.Amount;
		}
		#endregion

		#region Overrides
		/// <summary>
		/// Converts Money to currency representation.
		/// </summary>
		/// <returns>String representation.</returns>
		public override string ToString() {
			return m_amount.ToString("c");
		}

		/// <summary>
		/// Determines if the values of the two objects are equal.
		/// </summary>
		/// <param name="obj">Object to compare to.</param>
		/// <returns></returns>
		public override bool Equals(object obj) {
			return (this.CompareTo(obj) == 0);
		}

		/// <summary>
		/// Gets the hash code.
		/// </summary>
		/// <returns>Hashcode</returns>
		public override int GetHashCode() {
			return m_amount.GetHashCode();
		}
		#endregion

		#region Public properties
		/// <summary>
		/// Gets the amount.
		/// </summary>
		/// <value>Amount of money as a decimal.</value>
		public decimal Amount {
			get {
				return m_amount;
			}
		}
		#endregion

		#region Public methods
		/// <summary>
		/// Equally divides the <see cref="Money"/> instance "n" ways.
		/// </summary>
		/// <param name="n">Number of ways to split the <see cref="Money"/> instance.</param>
		public Money[] Allocate(int n) {
			Money lowResult = new Money(this.Amount / n);
			Money highResult = lowResult.Amount + 1;
			Money[] results = new Money[n];
			int remainder = (int)(this.Amount % n);
			for(int i=0; i<remainder; i++) {
				results[i] = highResult;
			}
			for(int i=remainder; i<n; i++) {
				results[i] = lowResult;
			}
			return results;
		}

		/// <summary>
		/// Divides the <see cref="Money"/> instance based on ratios.
		/// </summary>
		/// <param name="ratios">Array of integers representing the ratio to split the <see cref="Money"/> instance.</param>
		public Money[] Allocate(int[] ratios) {
			int total = 0;
			foreach(int ratio in ratios) {
				total += ratio;
			}
			decimal remainder = this.Amount;
			Money[] results = new Money[ratios.Length];
			for(int i=0; i<results.Length; i++) {
				results[i] = new Money(this.Amount * ratios[i] / total);
				remainder -= results[i].Amount;
			}
			for(int i=0; i<(int)remainder; i++) {
				results[i].m_amount++;
			}
			return results;
		}
		#endregion

		#region Private fields
		private decimal m_amount;
		private const int FRACTION_DIGITS = 2;
		#endregion

		#region Operators
		/// <summary>
		/// Adds two <see cref="Money"/> instances.
		/// </summary>
		/// <param name="m1">First instance.</param>
		/// <param name="m2">Second instance.</param>
		/// <returns>Sum of the <see cref="Money"/> instances.</returns>
		public static Money operator+(Money m1, Money m2) {
			return new Money(m1.Amount + m2.Amount);
		}

		/// <summary>
		/// Subtracts two <see cref="Money"/> instances.
		/// </summary>
		/// <param name="m1">First instance.</param>
		/// <param name="m2">Second instance.</param>
		/// <returns>Difference of the <see cref="Money"/> instances.</returns>
		public static Money operator-(Money m1, Money m2) {
			return new Money(m1.Amount - m2.Amount);
		}

		/// <summary>
		/// Multiplies two <see cref="Money"/> instances.
		/// </summary>
		/// <param name="m">First instance.</param>
		/// <param name="multiplier">Second instance.</param>
		/// <returns>Product of the <see cref="Money"/> instance.</returns>
		public static Money operator*(Money m, decimal multiplier) {
			return new Money(m.Amount * multiplier);
		}

		/// <summary>
		/// Divides two <see cref="Money"/> instances.
		/// </summary>
		/// <param name="m">First instance.</param>
		/// <param name="divisor">Second instance.</param>
		/// <returns>Dividend of the <see cref="Money"/> instance.</returns>
		public static Money operator/(Money m, decimal divisor) {
			return new Money(Math.Round(m.Amount / divisor, FRACTION_DIGITS));
		}

		/// <summary>
		/// Implicitly converts a <see cref="System.Decimal"/> to a <see cref="Money"/>.
		/// </summary>
		/// <param name="amount">Amount.</param>
		/// <returns>New <see cref="Money"/> instance.</returns>
		public static implicit operator Money(decimal amount) {
			return new Money(amount);
		}

		/// <summary>
		/// Determines if one <see cref="Money"/> instance is greater than another <see cref="Money"/> instance.
		/// </summary>
		/// <param name="m1">First instance.</param>
		/// <param name="m2">Second instance.</param>
		/// <returns>
		///		<c>true</c> if <paramref name="m1"/> is greater than <paramref name="m2"/>; otherwise, <c>false</c>.
		/// </returns>
		public static bool operator>(Money m1, Money m2) {
			return m1.Amount > m2.Amount;
		}

		/// <summary>
		/// Determines if one <see cref="Money"/> instance is greater than or equal to  another <see cref="Money"/> instance.
		/// </summary>
		/// <param name="m1">First instance.</param>
		/// <param name="m2">Second instance.</param>
		/// <returns>
		///		<c>true</c> if <paramref name="m1"/> is greater than or equal to <paramref name="m2"/>; otherwise, <c>false</c>.
		/// </returns>
		public static bool operator>=(Money m1, Money m2) {
			return m1.Amount >= m2.Amount;
		}

		/// <summary>
		/// Determines if one <see cref="Money"/> instance is less than another <see cref="Money"/> instance.
		/// </summary>
		/// <param name="m1">First instance.</param>
		/// <param name="m2">Second instance.</param>
		/// <returns>
		///		<c>true</c> if <paramref name="m1"/> is less than <paramref name="m2"/>; otherwise, <c>false</c>.
		/// </returns>
		public static bool operator<(Money m1, Money m2) {
			return m1.Amount < m2.Amount;
		}

		/// <summary>
		/// Determines if one <see cref="Money"/> instance is less than or equal to another <see cref="Money"/> instance.
		/// </summary>
		/// <param name="m1">First instance.</param>
		/// <param name="m2">Second instance.</param>
		/// <returns>
		///		<c>true</c> if <paramref name="m1"/> is less than or equal to <paramref name="m2"/>; otherwise, <c>false</c>.
		/// </returns>
		public static bool operator<=(Money m1, Money m2) {
			return m1.Amount <= m2.Amount;
		}

		/// <summary>
		/// Determines if two <see cref="Money"/> instances are equal.
		/// </summary>
		/// <param name="m1">First instance.</param>
		/// <param name="m2">Second instance.</param>
		/// <returns>
		///		<c>true</c> if <paramref name="m1"/> is equal to <paramref name="m2"/>; otherwise, <c>false</c>.
		/// </returns>
		public static bool operator==(Money m1, Money m2) {
			return m1.Amount == m2.Amount;
		}

		/// <summary>
		/// Determines if two <see cref="Money"/> instances are not equal.
		/// </summary>
		/// <param name="m1">First instance.</param>
		/// <param name="m2">Second instance.</param>
		/// <returns>
		///		<c>true</c> if <paramref name="m1"/> is not equal to <paramref name="m2"/>; otherwise, <c>false</c>.
		/// </returns>
		public static bool operator!=(Money m1, Money m2) {
			return m1.Amount != m2.Amount;
		}
		#endregion

		#region IComparable Members
		/// <summary>
		/// Compares two <see cref="Money"/> instances.
		/// </summary>
		/// <param name="obj"><see cref="Money"/> instance to compare to.</param>
		/// <returns>
		///    A signed number indicating the relative values of this instance and value.
		///    <list>
		///      <item>Less than zero if this instance is less than obj.</item>
		///      <item>Zero if this instance is equal to obj.</item>
		///      <item>Greater than zero if this instance is greater than obj.</item>
		///    </list>
		/// </returns>
		public int CompareTo(object obj) {
			if(!(obj is Money)) {
				throw new ArgumentException("Cannot compare Money to " + obj.GetType().FullName, "obj");
			}
			Money m = (Money)obj;
			return this.Amount.CompareTo(m.Amount);
		}
		#endregion
	}
}
